#include <iostream>
#include <poll.h>
#include <sys/select.h>
#include "Sock.hpp"

#define DFL -1
#define NUM 1024
static struct pollfd fdsArray[NUM];
using namespace std;

void user(std::string process)
{
    cout << "\nUsage: " << process << " port" << endl;
}

static void showArray(struct pollfd arr[], int num)
{
    cout << "当前合法sock list# ";
    for (int i = 0; i < num; i++)
    {
        if (arr[i].fd == DFL)
            continue;
        else
            cout << arr[i].fd << " ";
    }
    cout << endl;
}

// readfds: 现在包含就是已经就绪的sock
static void HandlerEvent(int listensock)
{
    for (int n = 0; n < NUM; ++n)
    {
        if (fdsArray[n].fd == DFL)
            continue;

        if (n == 0 && fdsArray[n].fd == listensock)
        {
            if (fdsArray[n].revents & POLLIN)
            {
                // 具有了一个新链接
                cout << "已经有一个新链接到来了，需要进行获取(读取/拷贝)了" << endl;
                string clientip;
                uint16_t clientport = 0;
                int sockfd = sock::Accept(listensock, &clientip, &clientport);
                if (sockfd < 0)
                {
                    // 获取链接失败
                    logMessage(WARINING, "accept: %s[%d]", strerror(errno), sockfd);
                    exit(CONN_ERR);
                }
                cout << "clientip: " << clientip << " clientport: " << clientport << endl;

                // read/write -- 不能，因为你read不知道底层数据是否就绪！！
                // 想办法把新的fd托管给fdsArray？如何托管？？
                int i = 0;
                for (; i < NUM; ++i)
                {
                    if (fdsArray[i].fd == DFL)
                        break;
                }

                if (i == NUM)
                {
                    cout << "服务器已经到了最大上限，无法再承载更多同时保持的连接了" << endl;
                    close(sockfd);
                }
                else
                {
                    fdsArray[i].fd = sockfd; // 将sock添加到select中，进行进一步的监听就绪事件了！
                    fdsArray[i].events = POLLIN;
                    fdsArray[i].revents = 0;
                }
            }
        } // end if (n == 0 && fdsArray[n] == listensock)
        else
        {
            if (fdsArray[n].revents & POLLIN)
            {
                // 一定是一个合法的普通的IO类sock就绪了
                // read/recv读取即可
                // TODO bug
                char buffer[1024];
                ssize_t s = recv(fdsArray[n].fd, buffer, sizeof(buffer) - 1, 0);
                if (s > 0)
                {
                    buffer[s] = 0;
                    cout << "client[" << fdsArray[n].fd << "]# " << buffer << endl;
                }
                else if (s == 0)
                {
                    cout << "client[" << fdsArray[n].fd << "] quit, server close " << fdsArray[n].fd << endl;
                    close(fdsArray[n].fd);
                    fdsArray[n].fd = DFL; // 去除对该文件描述符事件监听
                    fdsArray[n].events = 0;
                    fdsArray[n].revents = 0;
                    showArray(fdsArray, NUM);
                }
                else
                {
                    cout << "client[" << fdsArray[n].fd << "] error, server close " << fdsArray[n].fd << endl;
                    close(fdsArray[n].fd);
                    fdsArray[n].fd = DFL; // 去除对该文件描述符事件监听
                    fdsArray[n].events = 0;
                    fdsArray[n].revents = 0;
                    showArray(fdsArray, NUM);
                }
            }
        }
    }
}


// ./SelectServer 8080
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        user(argv[0]);
        exit(USAGE_ERR);
    }

    int listensock = sock::Socket();
    sock::Bind(listensock, atoi(argv[1]));
    sock::Listen(listensock);

    for (int i = 0; i < NUM; ++i)
    {
        fdsArray[i].fd = DFL;
        fdsArray[i].events = 0;
        fdsArray[i].revents = 0;
    }
    fdsArray[0].fd = listensock;
    fdsArray[0].events = POLLIN;
    while (true)
    {
        int timeout = 1000;
        // 如何看待监听socket，获取新连接的，本质需要先三次握手，前提给我发送syn -> 建立连接的本质，其实也是IO，一个建立好的
        // 连接我们称之为：读事件就绪！listensocket 只（也）需要关心读事件就绪！
        // accept: 等 + "数据拷贝"
        // int sock = Sock::Accept(listensock, );
        // 编写多路转接代码的时候，必须先保证条件就绪了，才能调用IO类函数！
        int n = poll(fdsArray, NUM, timeout);
        switch (n)
        {
        case 0:
            cout << "time out: " << (unsigned long)time(nullptr) << endl;
            break;
        case -1:
            cerr << errno << ": " << strerror(errno) << endl;
            break;
        default:
            HandlerEvent(listensock);
            // 等待成功
            break;
        }
    }

    return 0;
}
